#include <netinet/in.h>   
#include <sys/types.h>   
#include <sys/socket.h>   
#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
#include <unistd.h>
// #include "../file.h"
#include "../cmd.h"
#include <pthread.h>
#include <limits.h>
#include <dirent.h>
#include <errno.h>

#define CTRL_PORT 1023
#define DATA_PORT 20
#define BUFSIZE 1024

// int data_sock;
// sockaddr_in data_addr;
int pasv(Cmd *c,int& data_sock)
{
	data_sock=socket(AF_INET,SOCK_STREAM,0);
	if(data_sock<0)
	{
		printf("data socket failed\n");
		return -1;
	}
	sockaddr_in data_addr;
	data_addr.sin_family=AF_INET;
	data_addr.sin_port=htons(0);
	data_addr.sin_addr.s_addr=htons(INADDR_ANY);
	if(bind(data_sock,(sockaddr*)&data_addr,sizeof(data_addr)))
	{
		printf("data bind failed\n");
		return -1;
	}
	if(listen(data_sock,20))
	{
		printf("data listen failed\n");
		return -1;
	}

	struct sockaddr_in addr;
	socklen_t addr_len = sizeof(addr);
	memset(&addr, 0, sizeof (addr));
	if(getsockname(data_sock, (struct sockaddr *)&addr, &addr_len) != 0)
	{
	    printf("error when trying to get the addr\n");
	    return -1;
	}
	c->cmd=PASVSUC;
	c->port=addr.sin_port;
	// c->ip=addr.sin_addr.s_addr;
	c->ip=data_addr.sin_addr.s_addr;
	printf("cmd:%d port:%d ip:%d\n", c->cmd,c->port,c->ip);
	return 0;
}

int sendfile(char* filename,int& data_sock)
{
	if(data_sock<0)
	{
		printf("data_sock error\n");
		return -1;
	}
	sockaddr_in client_addr;
	socklen_t addrlen=sizeof(client_addr);
	int send_sock=accept(data_sock,(sockaddr*)&client_addr,&addrlen);
	if(send_sock<0)
	{
		printf("data accept failed %s\n",strerror(errno));
		return -1;
	}

	FILE *fp = fopen(filename, "r");  
	char buffer[BUFSIZE];
	if (fp == NULL)  
	{  
		printf("File:\t%s Not Found!\n", filename);  	
	}  
	else  
	{  
		bzero(buffer, BUFSIZE);  
		int file_block_length = 0;  
		while( (file_block_length = fread(buffer, sizeof(char), BUFSIZE, fp)) > 0)  
		{  
			printf("file_block_length = %d\n", file_block_length);  

			// 发送buffer中的字符串到new_sender_socket,实际上就是发送给客户端  
			if (send(send_sock, buffer, file_block_length, 0)!=file_block_length)  
			{  
				printf("Send File:\t%s Failed!\n", filename);  
				return -1;
			}  

			bzero(buffer, sizeof(buffer));  
		}  
		fclose(fp);  
		printf("File:\t%s Transfer Finished!\n", filename);  
	}  
	close(send_sock);
	close(data_sock);
	return 0;
}
int recvfile(char* filename,int& data_sock)
{
	if(data_sock<0)
	{
		printf("data_sock error\n");
		return -1;
	}
	sockaddr_in client_addr;
	socklen_t addrlen=sizeof(client_addr);
	int recv_sock=accept(data_sock,(sockaddr*)&client_addr,&addrlen);
	if(recv_sock<0)
	{
		printf("data accpet failed %s\n",strerror(errno));
		return -1;
	}
	FILE* fp=fopen(filename,"w");
	if(!fp)
	{
		printf("failed to open %s\n",filename);
		return -1;
	}
	char buffer[BUFSIZE];
	bzero(buffer, sizeof(buffer));  
	int length = 0;  
	while(length = recv(recv_sock, buffer, BUFSIZE, 0))  
	{  
		if (length < 0)  
		{  
			printf("Recieve Data From sender Failed!\n");  
			return -1;
		}  

		int write_length = fwrite(buffer, sizeof(char), length, fp);  
		if (write_length < length)  
		{  
			printf("File:\t%s Write Failed!\n", filename);  
			return -1;
		}  
		bzero(buffer, BUFSIZE);  
	}  

	printf("Recieve File:\t %s From sender[%d] Finished!\n", filename,client_addr.sin_addr.s_addr);  
	fclose(fp);
	close(recv_sock);
	close(data_sock);
	return 0;
}

void pwd(char* curpath,int len)
{
	getcwd(curpath,len);
	printf("get pwd %s\n", curpath);
}

int cwd(char* subpath)
{
	return chdir(subpath);
}

int dir(char* result)
{
	DIR *d=NULL;
	char buf[4096];
	pwd(buf,4096);
	d=opendir(buf);
	if(d==NULL)
	{
		printf("open dir failed");
		return -1;
	}
	dirent* ent=NULL;
	char r[4096]="";
	while((ent=readdir(d))!=NULL)
	{
		// printf("reclen=%d type=%d\t\n",ent->d_reclen,ent->d_type );
		// printf("%s\n",ent->d_name );
		strcat(r,ent->d_name);
		strcat(r,"\n");
	}
	strcpy(result,r);
	return 0;
}

void quit()
{
	printf("user exit\n");
}

int process_client_request(int new_server_socket)
{
	int data_sock;
	char cmd[4096];
	char buf[4096];
	while(true)
	{
		printf(">\n");
		if(recv(new_server_socket,cmd,sizeof(Cmd),0)<=0)
		{
			printf("recv cmd failed\n");
			return -1;
		}
		Cmd *c=(Cmd*)cmd;
		printf("cmd:%d\nport:%dname:%s\n",c->cmd,c->port,c->name);
		if(c->cmd==PWD)
		{
			pwd(buf,4096);
			if(send(new_server_socket,(void*)buf,sizeof(buf),0)<=0)
			{
				printf("send pwd failed\n");
				exit(-1);
			}					
		}
		else if(c->cmd==LISTFILES)
		{
			if(dir(buf)!=0)
			{
				printf("dir failed\n");
				exit(1);
			}
			if(send(new_server_socket,(void*)buf,sizeof(buf),0)<=0)
			{
				printf("send dir failed\n");
				exit(1);
			}
		}
		else if(c->cmd==CWD)
		{
			if(cwd(c->name)!=0)
			{
				printf("change work path failed\n");
				exit(1);
			}
			pwd(buf,4096);
			if((send(new_server_socket,(void*)buf,sizeof(buf),0))<=0)
			{
				printf("send pwd failed\n");
				exit(1);
			}
		}
		else if(c->cmd==PASV)
		{
			Cmd a;
			if(pasv(&a,data_sock)==-1)
			{
				printf("pasv failed\n");
				exit(1);
			}
			if(send(new_server_socket,(void*)&a,sizeof(Cmd),0)<=0)
			{
				printf("send pasv failed\n");
				exit(1);
			}
		}
		else if(c->cmd==PUT)
		{
			if(recvfile(c->name,data_sock)!=0)
			{
				printf("recv file %s failed\n",c->name);
				exit(1);
			}
		}
		else if(c->cmd==GET)
		{
			if(sendfile(c->name,data_sock)!=0)
			{
				printf("send file %s failed\n", c->name);
				exit(1);
			}
		}
		else if(c->cmd==QUIT)
		{
			printf("user exited\n");
			break;
		}
		else
		{
			printf("unknown cmd\n");
			printf("cmd:%d\nport:%d\nname:%s\n",c->cmd,c->port,c->name);
		}
	}
	return 0;
}

void* thread_func(void* param)
{
	int* sock_param=(int*)param;
	if(process_client_request(*sock_param)==-1)
	{
		printf("process client request failed\n");
	}
	return 0;
}

int main(int argc, char **argv)  
{
	sockaddr_in server_addr;
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family=AF_INET;
	server_addr.sin_addr.s_addr=htons(INADDR_ANY);
	server_addr.sin_port=htons(CTRL_PORT);

	int server_socket=socket(AF_INET,SOCK_STREAM,0);
	if(server_socket<0)
	{
		printf("Create Server Socket Failed\n");
		exit(1);
	}  
	bool isReusable=true;
    	setsockopt(server_socket,SOL_SOCKET ,SO_REUSEADDR,(const char*)&isReusable,sizeof(bool));

	if(bind(server_socket,(sockaddr*)&server_addr,sizeof(server_addr)))
	{
		printf("Server Bind Port:%d Failed %s\n",CTRL_PORT,strerror(errno));
		exit(1);
	}

	if(listen(server_socket,20))
	{
		printf("server listen failed\n");
		exit(1);
	}

	printf("waiting for connect\n");
	/////////////////////////
	while(true)
	{
		sockaddr_in client_addr;
		socklen_t length=sizeof(client_addr);
		int new_server_socket=accept(server_socket,(sockaddr*)&client_addr,&length);
		if(new_server_socket<0)
		{
			printf("server accept failed\n");
			exit(1);
		}
		pthread_t* handle=new pthread_t;

		pthread_create(handle,NULL,thread_func,(void*)&new_server_socket);
		
	}

	close(server_socket);

	return 0;  
}  
